#!/usr/bin/env python
""" AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2014  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Exported Classes:

    PreferenceDialog -- a dialog that allows users to set various preferences for AnyBackup
    listdialog -- a custom dialog which allows a user to provide regular expressions for file extensions to include 
                  filenames to exclude.
    remoteDialog -- a dialog that allows users to specify the remote connection + path details for a network drive
                    when using remote indexing
    SearchDialog -- a dialog that allows users to search through the database for matching files + directories
    manageDirDialog -- a dialog that allows users to control what folders will be backed up from a given content drive
    DriveSummary -- A dialog that displays a summary of one or more drives, giving a textual and graphical view of total + used space
    """
import re
import os

import wx
import wx.lib.agw.aui as aui
import wx.lib.agw.piectrl as PC
import DriveUtil
from enum import AEnums
from sql import SQLLite
from easyListCtrl import DictView
import ConfigParser
from backupManager import backupManager


class PreferenceDialog(wx.Dialog):
    """
    A dialog that allows users to set various preferences for AnyBackup
    """
    def __init__(self, parent, homeDir):
        wx.Dialog.__init__(self, parent, -1, 'Preferences', wx.DefaultPosition, wx.Size(175, 150))
        self.config = ConfigParser.SafeConfigParser(backupManager.DEFAULTS)
        self.homeDir = homeDir
        self.config.read(os.path.join(homeDir, 'AnyBackup.cfg'))
        modePanel = wx.Panel(self, -1)
        portPanel = wx.Panel(self, -1)
        nagPanel = wx.Panel(self, -1)
        buttonPanel = wx.Panel(self, -1)
        mainBox = wx.BoxSizer(wx.VERTICAL)
        modeBox = wx.BoxSizer(wx.HORIZONTAL)
        portBox = wx.BoxSizer(wx.HORIZONTAL)
        nagBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        modeTip = wx.ToolTip('Drive selection aglorithm for backup, balanced'
            ' evenly distributes files, sticky clumps files based on dir.')
        portTip = wx.ToolTip('For remote indexing, corresponds with whatever port you set the anybackupserver to'
            'to run on your linux host.')
        nagTip = wx.ToolTip('>0 means that AnyBackup will nag you to run a chkdsk on your drives every x months.')
        self.modeBox = wx.ComboBox(modePanel, -1, self.config.get('anybackup', 'drive-selection'),
                                   choices=[backupManager.BALANCED, backupManager.STICKY], size=wx.Size(80, -1),
                                   style=wx.CB_READONLY)
        self.modeBox.SetToolTip(modeTip)
        self.portCtrl = wx.TextCtrl(portPanel, -1, self.config.get('anybackup', 'pyro-port'), wx.DefaultPosition,
                                    size=wx.Size(80, -1))
        self.portCtrl.SetToolTip(portTip)
        self.nagCBox = wx.ComboBox(nagPanel, -1, self.config.get('anybackup', 'nag-length'),
                                   choices=[str(x) for x in range(0, 13)], size=wx.Size(80, -1),
                                   style=wx.CB_READONLY)
        self.nagCBox.SetToolTip(nagTip)
        self.modeLabel = wx.StaticText(modePanel, -1, 'Backup Mode', wx.DefaultPosition, size=wx.Size(50, -1))
        self.portLabel = wx.StaticText(portPanel, -1, 'Pyro Port', wx.DefaultPosition, size=wx.Size(50, -1))
        self.nagLabel = wx.StaticText(nagPanel, -1, 'Chkdsk Reminder', wx.DefaultPosition, size=wx.Size(50, -1))
        self.save = wx.Button(buttonPanel, -1, "Save")
        self.cancel = wx.Button(buttonPanel, -1, "Cancel")
        modeBox.Add(self.modeLabel, 0, wx.ALL, 5)
        modeBox.Add(self.modeBox, 0, wx.ALL, 5)
        portBox.Add(self.portLabel, 0, wx.ALL, 5)
        portBox.Add(self.portCtrl, 0, wx.ALL, 5)
        nagBox.Add(self.nagLabel, 0, wx.ALL, 5)
        nagBox.Add(self.nagCBox, 0, wx.ALL, 5)
        buttonBox.Add(self.save, 0, wx.LEFT, 5)
        buttonBox.Add(self.cancel)
        modePanel.SetSizer(modeBox)
        portPanel.SetSizer(portBox)
        nagPanel.SetSizer(nagBox)
        buttonPanel.SetSizer(buttonBox)
        mainBox.Add(modePanel, 0)
        mainBox.Add(portPanel, 0)
        mainBox.Add(nagPanel, 0)
        mainBox.Add(buttonPanel, 0)
        self.SetSizer(mainBox)
        self.save.Bind(wx.EVT_BUTTON, self.savePrefs)
        self.cancel.Bind(wx.EVT_BUTTON, self.closeDialog)

    # noinspection PyUnusedLocal
    def savePrefs(self, evt):
        """
        Writes out the AnyBackup.cfg preferences file
        """
        try:
            int(self.portCtrl.GetValue())
        except ValueError:
            wx.MessageDialog(self, "Pyro port must be a number!", "Invalid Port", wx.OK).ShowModal()
            return
        self.config.set('anybackup', 'pyro-port', self.portCtrl.GetValue())
        self.config.set('anybackup', 'drive-selection', self.modeBox.GetValue())
        self.config.set('anybackup', 'nag-length', self.nagCBox.GetValue())
        with open(os.path.join(self.homeDir, 'AnyBackup.cfg'), 'w') as f:
            self.config.write(f)
        self.EndModal(wx.ID_OK)

    def getConfig(self):
        return self.config

    # noinspection PyUnusedLocal
    def closeDialog(self, evt):
        self.EndModal(wx.ID_CANCEL)


class remoteDialog(wx.Dialog):
    """
    A dialog that allows users to specify the remote connection + path details for a network drive
    when using remote indexing
    """
    def __init__(self, parent, driveRef):
        self.r = SQLLite()
        self._drive = driveRef
        self._host = self._drive.get(AEnums.REMOTEHOST, None)
        self._path = self._drive.get(AEnums.REMOTEPATH, None)
        label = '%s - Remote Index' % self._drive[AEnums.NAME]
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(200, 135))
        hostStr = self._drive.get(AEnums.REMOTEHOST, '')
        pathStr = self._drive.get(AEnums.REMOTEPATH, '')
        hostPanel = wx.Panel(self, -1)
        pathPanel = wx.Panel(self, -1)
        buttonPanel = wx.Panel(self, -1)
        mainBox = wx.BoxSizer(wx.VERTICAL)
        hostBox = wx.BoxSizer(wx.HORIZONTAL)
        pathBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        self.hostCtrl = wx.TextCtrl(hostPanel, -1, hostStr, wx.DefaultPosition)
        self.hostCtrl.SetToolTip(wx.ToolTip('IP anybackupserver is running on. (ex. 192.168.1.200)'))
        self.pathCtrl = wx.TextCtrl(pathPanel, -1, pathStr, wx.DefaultPosition)
        self.pathCtrl.SetToolTip(wx.ToolTip('Path of Samba share on remote host (ex. /path/to/share'))
        self.hostLabel = wx.StaticText(hostPanel, -1, 'Remote Host', wx.DefaultPosition)
        self.pathLabel = wx.StaticText(pathPanel, -1, 'Remote Path', wx.DefaultPosition)
        self.save = wx.Button(buttonPanel, -1, "Save")
        self.cancel = wx.Button(buttonPanel, -1, "Cancel")
        hostBox.Add(self.hostLabel, 0, wx.ALL, 5)
        hostBox.Add(self.hostCtrl, 0, wx.ALL, 5)
        pathBox.Add(self.pathLabel, 0, wx.ALL, 5)
        pathBox.Add(self.pathCtrl, 0, wx.ALL, 5)
        buttonBox.Add(self.save, 0, wx.LEFT, 5)
        buttonBox.Add(self.cancel)
        hostPanel.SetSizer(hostBox)
        pathPanel.SetSizer(pathBox)
        buttonPanel.SetSizer(buttonBox)
        mainBox.Add(hostPanel, 0)
        mainBox.Add(pathPanel, 0)
        mainBox.Add(buttonPanel, 0)
        self.SetSizer(mainBox)
        self.save.Bind(wx.EVT_BUTTON, self.saveRemote)
        self.cancel.Bind(wx.EVT_BUTTON, self.closeDialog)

    # noinspection PyUnusedLocal
    def saveRemote(self, evt):
        """
        Sets remote values for the drive and writes it to the sqlite database
        """
        self._drive[AEnums.REMOTEHOST] = self.hostCtrl.GetValue()
        self._drive[AEnums.REMOTEPATH] = self.pathCtrl.GetValue()
        self.r.updateDrive(self._drive)
        self.Close(1)

    # noinspection PyUnusedLocal
    def closeDialog(self, evt):
        self.Close(1)


class SearchDialog(wx.Dialog):
    """
    a dialog that allows users to search through the database for matching files + directories
    """
    CONTAINS = 'Contains'
    SQL = 'SQL'
    ENDS = 'Ends With'
    STARTS = 'Starts With'
    CHOICES = [CONTAINS, SQL, ENDS, STARTS]
    TYPECHOICES = [AEnums.DIR, AEnums.FILE, AEnums.ALL]
    DRIVECHOICES = [AEnums.BACKUP, AEnums.CONTENT, AEnums.ALL]

    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, -1, 'Search', wx.DefaultPosition, wx.Size(250, 350))
        bagSizer = wx.GridBagSizer(hgap=5, vgap=5)
        self.queryChoice = wx.ComboBox(self, -1, self.CONTAINS, choices=self.CHOICES,
                                       style=wx.CB_READONLY | wx.CB_SORT | wx.CB_DROPDOWN)
        self.drives = parent.getDrives()
        self.driveChoices = ['{0}-{1}'.format(x[AEnums.NAME], x[AEnums.SERIAL]) for x in self.drives] + [AEnums.ALL]
        self.drives += [None]
        self.driveChoice = wx.ComboBox(self, -1, AEnums.ALL, choices=self.driveChoices,
                                       style=wx.CB_READONLY | wx.CB_DROPDOWN)
        self.type = wx.RadioBox(self, -1, 'File Type', choices=self.TYPECHOICES, style=wx.HORIZONTAL)
        self.type.SetSelection(self.TYPECHOICES.index(AEnums.ALL))
        self.dtype = wx.RadioBox(self, -1, 'Drive Type', choices=self.DRIVECHOICES, style=wx.HORIZONTAL)
        self.dtype.SetSelection(self.DRIVECHOICES.index(AEnums.ALL))
        self.queryBox = wx.TextCtrl(self, -1, '')
        ok = wx.Button(self, wx.ID_OK, 'Ok')
        cancel = wx.Button(self, wx.ID_CANCEL, 'Cancel')
        bagSizer.Add(wx.StaticText(self, -1, 'Query'), pos=(0, 0), border=5, flag=wx.LEFT | wx.TOP)
        bagSizer.Add(self.queryBox, pos=(0, 1), border=5, flag=wx.RIGHT | wx.TOP | wx.EXPAND)
        bagSizer.Add(wx.StaticText(self, -1, 'Query Type'), pos=(1, 0), border=5, flag=wx.LEFT)
        bagSizer.Add(self.queryChoice, pos=(1, 1), border=5, flag=wx.RIGHT | wx.EXPAND)
        bagSizer.Add(wx.StaticText(self, -1, 'Drive'), pos=(2, 0), border=5, flag=wx.LEFT)
        bagSizer.Add(self.driveChoice, pos=(2, 1), border=5, flag=wx.RIGHT | wx.EXPAND)
        bagSizer.Add(self.type, pos=(3, 0), span=(1, 2), border=5, flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
        bagSizer.Add(self.dtype, pos=(4, 0), span=(1, 2), border=5, flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
        bagSizer.Add(ok, pos=(5, 0), border=5, flag=wx.LEFT | wx.BOTTOM)
        bagSizer.Add(cancel, pos=(5, 1), border=5, flag=wx.RIGHT | wx.BOTTOM)
        self.SetSizer(bagSizer)
        self.Fit()
        self.Bind(wx.EVT_BUTTON, self.returnQuery, ok)
        self.Bind(wx.EVT_BUTTON, self.cancel, cancel)
        self.queryBox.SetFocus()
        self.value = None

    # noinspection PyUnusedLocal
    def returnQuery(self, evt=None):
        """
        Builds a query based off what options and input a user has selected
        """
        fileType = self.TYPECHOICES[self.type.GetSelection()]
        driveType = self.DRIVECHOICES[self.dtype.GetSelection()]
        drive = self.drives[self.driveChoices.index(self.driveChoice.GetValue())]
        queryType = self.queryChoice.GetValue()
        query = self.queryBox.GetValue()
        if queryType == self.CONTAINS:
            queryStr = '%{0}%'.format(query)
        elif queryType == self.SQL:
            queryStr = query
        elif queryType == self.ENDS:
            queryStr = '%{0}'.format(query)
        else:
            queryStr = '{0}%'.format(query)
        self.value = {
            AEnums.DRIVE: drive,
            AEnums.DRIVES: driveType,
            AEnums.TYPE: fileType,
            AEnums.QUERY: queryStr,
        }
        self.EndModal(wx.ID_OK)

    # noinspection PyUnusedLocal
    def cancel(self, evt=None):
        self.EndModal(wx.ID_CANCEL)

    def GetValue(self):
        return self.value


class manageDirDialog(wx.Dialog):
    """
    a dialog that allows users to control what folders will be backed up from a given content drive
    """
    def __init__(self, parent, driveRef):
        self._drive = driveRef
        self.r = SQLLite()
        self._dirList = self.r.getPaths(self._drive)
        label = '%s - Directories' % driveRef[AEnums.NAME]
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(250, 350))
        self.mgr = aui.AuiManager(self)
        self.dirBox = wx.ListBox(self, -1, style=wx.LB_EXTENDED)
        for directory in self._dirList:
            self.dirBox.Insert(str(directory), 0, None)
        buttonPanel = wx.Panel(self, -1, size=(-1, 32))
        addDir = wx.Button(buttonPanel, -1, 'Add...')
        removeDir = wx.Button(buttonPanel, -1, 'Remove')
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox.Add(addDir, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.TOP, 5)
        buttonBox.Add(removeDir, 0, wx.BOTTOM | wx.RIGHT | wx.TOP, 5)
        buttonPanel.SetSizer(buttonBox)
        self.mgr.AddPane(self.dirBox,
                         aui.AuiPaneInfo().Name('Directories').Floatable(False).Caption('Directories').Gripper(
                             False).Center().CloseButton(False))
        self.mgr.AddPane(buttonPanel,
                         aui.AuiPaneInfo().Name('Buttons').Gripper(False).CaptionVisible(False).Bottom().CloseButton(
                             False))
        self.mgr.Update()
        self.Bind(wx.EVT_BUTTON, self.removeDir, removeDir)
        self.Bind(wx.EVT_BUTTON, self.addDir, addDir)

    def removeDir(self, evt):
        """
        Unselect a folder for backup on a content drive
        """
        remove = self.dirBox.GetSelections()
        selectList = self._dirList[:]
        selectList.reverse()
        for index in remove:
            with self.r.batch():
                self.r.removePath(self._drive, selectList[index])
        self.r.commit()
        self._dirList = self.r.getPaths(self._drive)
        self.changeList(evt)

    def owns(self, dirIn):
        """
        Checks if a provided file path belongs to the content drive the dialog is set for

        dirIn -- a file path to check
        """
        letter = DriveUtil.getLetter(self._drive[AEnums.NAME], self._drive[AEnums.SERIAL])
        if re.search(r'^%s' % letter, dirIn, re.IGNORECASE):
            if os.path.exists(dirIn):
                return True
        return False

    def addDir(self, evt):
        """
        Adds a directory on a content drive to backup
        """
        dialog = wx.DirDialog(self, message='Pick a directory to index.')
        if dialog.ShowModal() == wx.ID_OK:
            if self.owns(dialog.GetPath()):
                result = self.r.addPath(self._drive, dialog.GetPath().strip(os.sep))
                self._dirList = self.r.getPaths(self._drive)
                if not result:
                    error = wx.MessageDialog(self, 'Error Adding Dir, is it a subdir of an already added directory?',
                                             'Add Error', wx.OK | wx.ICON_ERROR)
                    error.ShowModal()
                elif result == AEnums.OVERWRITE:
                    warn = wx.MessageDialog(self,
                                            'The directory added contained one or more of the subdirectories ' +
                                            'already present, sub dirs removed.',
                                            'Overwrote Subdir(s)', wx.OK | wx.ICON_WARNING)
                    warn.ShowModal()
                    self.changeList(evt)
                else:
                    self.changeList(evt)

    # noinspection PyUnusedLocal
    def changeList(self, event):
        """
        Populates the dir list with the selected directories
        """
        self.dirBox.Clear()
        for item in self._dirList:
            self.dirBox.Insert(item, 0, None)


class listdialog(wx.Dialog):
    """ a custom dialog which allows a user to provide regular expressions for file extensions
        to include filenames to exclude.
    Public functions:
    close -- closes the dialog"""

    def __init__(self, parent, buManager):
        self.buObject = buManager
        skipList = buManager.getSkipList()
        label = "Extensions and Skiplist Management"
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(250, 350))
        listSelPanel = wx.Panel(self)
        buttonPanel = wx.Panel(self)
        listSelBox = wx.BoxSizer(wx.HORIZONTAL)
        buttonBox = wx.BoxSizer(wx.HORIZONTAL)
        mainBox = wx.BoxSizer(wx.VERTICAL)
        self.ids = {'listLabel': 1, 'listSelect': 2, 'listBox': 3, 'add': 4, 'remove': 5, 'cancel': 6}
        self.listLabel = wx.StaticText(listSelPanel, self.ids['listLabel'], "List")
        self.listSelect = wx.ComboBox(listSelPanel, self.ids['listSelect'], "Skip", wx.DefaultPosition, wx.DefaultSize,
                                      ['Skip', 'Valid Ext'])
        self.listBox = wx.ListBox(self, self.ids['listBox'], style=wx.LB_EXTENDED)
        self.add = wx.Button(buttonPanel, self.ids['add'], "Add")
        self.remove = wx.Button(buttonPanel, self.ids['remove'], "Remove")
        self.cancel = wx.Button(buttonPanel, self.ids['cancel'], "Close")
        listSelBox.Add(self.listLabel, 0, wx.LEFT | wx.TOP | wx.RIGHT, 8)
        listSelBox.Add(self.listSelect, 0, wx.TOP | wx.RIGHT, 5)
        listSelPanel.SetSizer(listSelBox)
        buttonBox.Add(self.add, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
        buttonBox.Add(self.remove, 0, wx.BOTTOM | wx.RIGHT, 5)
        buttonBox.Add(self.cancel, 0, wx.BOTTOM | wx.RIGHT, 5)
        buttonPanel.SetSizer(buttonBox)
        mainBox.Add(listSelPanel, 0)
        mainBox.Add(self.listBox, 1, wx.ALL | wx.EXPAND, 5)
        mainBox.Add(buttonPanel, 0)
        self.SetSizer(mainBox)
        wx.EVT_BUTTON(self, self.ids['add'], self.addStr)
        wx.EVT_BUTTON(self, self.ids['remove'], self.removeStr)
        wx.EVT_BUTTON(self, self.ids['cancel'], self.close)
        wx.EVT_COMBOBOX(self, self.ids['listSelect'], self.changeList)
        for skip in skipList:
            self.listBox.Insert(skip, 0, None)

    # noinspection PyUnusedLocal
    def close(self, event):
        """ closes the dialog """
        self.Close(1)

    # noinspection PyBroadException
    def addStr(self, event):
        """ promps for a regular expression and adds it to the skip list or the valid extension list """
        dialog = wx.TextEntryDialog(self, "Enter new " + self.listSelect.GetValue(),
                                    "Add " + self.listSelect.GetValue(), "", style=wx.OK | wx.CANCEL)
        if dialog.ShowModal() == wx.ID_CANCEL:
            return
        else:
            add = dialog.GetValue()
            try:
                # A naive regular expression validator -- the assumption being that if the regex is invalid an
                # exception will be thrown
                re.search(add, 'test')
            except:
                wx.MessageDialog(self, 'The regular expression you entered is invalid.', 'Invalid Regex!',
                                 wx.OK | wx.ICON_ERROR).ShowModal()
            else:
                if re.search("skip", self.listSelect.GetValue(), re.IGNORECASE):
                    self.buObject.addSkip(add)
                else:
                    self.buObject.addValidExt(add)
                self.changeList(event)

    def getList(self):
        """ returns the current skip list or valid extension list """
        if re.search("skip", self.listSelect.GetValue(), re.IGNORECASE):
            currList = self.buObject.getSkipList()
        else:
            currList = self.buObject.getValidExt()
        return currList

    def removeStr(self, event):
        """ removes a given string from the skip lits or the valid extension list """
        remove = [self.listBox.GetString(x) for x in self.listBox.GetSelections()]
        currList = self.getList()
        for str in sorted(remove, reverse=True):
            currList.remove(str)
        if re.search("skip", self.listSelect.GetValue(), re.IGNORECASE):
            self.buObject.setSkipList(currList)
        else:
            self.buObject.setValidExt(currList)
        self.changeList(event)

    # noinspection PyUnusedLocal
    def changeList(self, event):
        """ populates the listbox with the skip list of the valid extension list depending on whath as been selected """
        currList = self.getList()
        self.listBox.Clear()
        for item in currList:
            self.listBox.Insert(item, 0, None)


class DriveInfo(wx.Dialog):
    def __init__(self, parent, drive):
        label = "{0} - {1}".format(drive[AEnums.NAME], drive[AEnums.SERIAL])
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(250, 325))
        mainBox = wx.BoxSizer(wx.VERTICAL)

        dataView = DictView(self, size=wx.Size(240, 90))

        letter = DriveUtil.getLetter(
            drive[AEnums.NAME], drive[AEnums.SERIAL]
        )

        dataView.updateDict([
            ('Drive Type', drive[AEnums.TYPE]),
            ('Drive Letter', letter),
            ('Total Space', '{0:.2f}GB'.format(drive[AEnums.TOTALSPACE]/1024**2)),
            ('Files', len(parent.buManager.r.getFilesByDrive(drive, AEnums.FILES))),
            ('Dirs', len(parent.buManager.r.getFilesByDrive(drive, AEnums.DIR)))
        ])

        spacePie = PC.PieCtrl(self, -1, size=wx.Size(250, 200))
        spacePie.SetBackColour(self.GetBackgroundColour())
        spacePie.GetLegend().SetTransparent(True)
        spacePie.GetLegend().SetHorizontalBorder(10)
        spacePie.GetLegend().SetWindowStyle(wx.STATIC_BORDER)
        spacePie.GetLegend().SetLabelFont(
            wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL,
                    False, "Courier New")
        )
        spacePie.GetLegend().SetPosition(wx.Point(0, 4))
        legend = spacePie.GetLegend()
        legend.SetBackColour('white')
        legend.SetTransparent(False)
        legend.SetSize(wx.Size(225, 30))
        legend.Reparent(self)

        spacePie.SetAngle(.4)
        setPart = PC.PiePart()
        setUsed = parent.buManager.r.getUsageByDrive(drive)/1024**2
        setPart.SetLabel('Set {0} ({1:.2f}GB)'.format(drive[AEnums.SETNAME], setUsed))
        setPart.SetValue(setUsed)
        setPart.SetColour(wx.Colour(255, 64, 249))
        spacePie._series.append(setPart)
        usedPart = PC.PiePart()
        used = (drive[AEnums.TOTALSPACE] - drive[AEnums.FREESPACE])/1024**2 - setUsed
        usedPart.SetLabel('Other ({0:.2f}GB)'.format(used))
        usedPart.SetValue(used)
        usedPart.SetColour(wx.Colour(209, 209, 209))
        spacePie._series.append(usedPart)
        freePart = PC.PiePart()
        free = drive[AEnums.FREESPACE]/1024**2
        freePart.SetLabel('Free Space ({0:.2f}GB)'.format(free))
        freePart.SetValue(free)
        freePart.SetColour(wx.Colour(51, 68, 245))
        spacePie._series.append(freePart)

        close = wx.Button(self, -1, "Close")
        mainBox.Add(legend, 0, wx.LEFT | wx.RIGHT | wx.TOP, 5)
        mainBox.Add(spacePie, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
        # mainBox.AddSpacer((220, 40))
        mainBox.Add(dataView, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
        mainBox.Add(close, 0, wx.ALL | wx.ALIGN_RIGHT, 5)
        self.SetSizer(mainBox)
        self.Bind(wx.EVT_BUTTON, self.done, close)
        dataView.Refresh()

    # noinspection PyUnusedLocal
    def done(self, evt):
        self.Close()


class DriveSummary(wx.Dialog):
    """
    A dialog that displays a summary of one or more drives, giving a textual and graphical view of total + used space
    """
    def __init__(self, parent, drives):
        label = 'Summary'
        wx.Dialog.__init__(self, parent, -1, label, wx.DefaultPosition, wx.Size(300, 500))
        mainBox = wx.BoxSizer(wx.VERTICAL)

        dataView = DictView(self, size=wx.Size(290, 200))
        dataView.updateDict([
            ('Summary', ''),
        ], color="#AAAAAA")
        dataView.updateDict([
            ('Total Space', '{0:.2f}GB'.format(sum([x[AEnums.TOTALSPACE] for x in drives])/1024**2)),
            ('Free Space', '{0:.2f}GB'.format(sum([x[AEnums.FREESPACE] for x in drives])/1024**2)),
            ('Files', sum([len(parent.buManager.r.getFilesByDrive(x, AEnums.FILES)) for x in drives])),
            ('Dirs', sum([len(parent.buManager.r.getFilesByDrive(x, AEnums.DIR)) for x in drives]))
        ], color="#D4D4D4")
        dataView.updateDict([
            ('Drive Totals', ''),
        ], color="#AAAAAA")
        for drive in sorted(drives, key=lambda x: x[AEnums.TOTALSPACE], reverse=True):
            dataView.updateDict([
                ('%s Total' % drive[AEnums.NAME], '{0:.2f}GB'.format(drive[AEnums.TOTALSPACE]/1024**2)),
            ])
        dataView.updateDict([
            ('Drive Free', ''),
        ], color="#AAAAAA")
        for drive in sorted(drives, key=lambda x: x[AEnums.FREESPACE], reverse=True):
            dataView.updateDict([
                ('%s Free' % drive[AEnums.NAME], '{0:.2f}GB'.format(drive[AEnums.FREESPACE]/1024**2)),
            ])

        spacePie = PC.PieCtrl(self, -1, size=wx.Size(500, 400))
        spacePie.SetBackColour(self.GetBackgroundColour())
        spacePie.GetLegend().SetTransparent(True)
        spacePie.GetLegend().SetHorizontalBorder(10)
        spacePie.GetLegend().SetWindowStyle(wx.STATIC_BORDER)
        spacePie.GetLegend().SetLabelFont(
            wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL,
                    False, "Courier New")
        )
        spacePie.GetLegend().SetPosition(wx.Point(0, 4))
        legend = spacePie.GetLegend()
        legend.SetBackColour('white')
        legend.SetTransparent(False)
        legend.SetSize(wx.Size(225, 30))
        legend.Reparent(self)

        spacePie.SetAngle(.4)
        setPart = PC.PiePart()
        setUsed = sum([parent.buManager.r.getUsageByDrive(x) for x in drives])/1024**2
        setPart.SetLabel('Set {0} ({1:.2f}GB)'.format(drives[0][AEnums.SETNAME], setUsed))
        setPart.SetValue(setUsed)
        setPart.SetColour(wx.Colour(255, 64, 249))
        spacePie._series.append(setPart)
        usedPart = PC.PiePart()
        used = (sum([x[AEnums.TOTALSPACE] - x[AEnums.FREESPACE] for x in drives]))/1024**2 - setUsed
        usedPart.SetLabel('Other ({0:.2f}GB)'.format(used))
        usedPart.SetValue(used)
        usedPart.SetColour(wx.Colour(209, 209, 209))
        spacePie._series.append(usedPart)
        freePart = PC.PiePart()
        free = sum([x[AEnums.FREESPACE] for x in drives])/1024**2
        freePart.SetLabel('Free Space ({0:.2f}GB)'.format(free))
        freePart.SetValue(free)
        freePart.SetColour(wx.Colour(51, 68, 245))
        spacePie._series.append(freePart)

        close = wx.Button(self, -1, "Close")
        mainBox.Add(legend, 0, wx.LEFT | wx.RIGHT | wx.TOP, 5)
        mainBox.Add(spacePie, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
        # mainBox.AddSpacer((220, 40))
        mainBox.Add(dataView, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
        mainBox.Add(close, 0, wx.ALL | wx.ALIGN_RIGHT, 5)
        self.SetSizer(mainBox)
        self.Bind(wx.EVT_BUTTON, self.done, close)
        dataView.Refresh()
        dataView.adjust()

    # noinspection PyUnusedLocal
    def done(self, evt):
        self.Close()